package core

import (
	"io"
	"os"
	"sync"
	"sync/atomic"
)

// 配置常量
const (
	blockSize      = 4 * 1024 * 1024 // 数据块大小
	maxConcurrency = 4               // 最大并发处理数
	bufferWindow   = 8               // 顺序写入缓冲窗口

)

// Encryptor 增强型加密器，一次完整加密的全部数据
type Encryptor struct {
	pwd           string          //密码
	step          int64           // 数据处理的长度，是密码的整数倍
	readChan      chan *readData  // 数据读取通道
	writeChan     chan *writeData // 数据写入通道，作用第一个数据是发送文件大小信息，其他情况下是处理异常
	sequenceChan  chan *writeData // 顺序控制通道
	cancelChan    chan struct{}   // 取消信号
	workerGroup   sync.WaitGroup  // 工作协程组
	bufferPool    sync.Pool       // 原始数据缓冲区池
	encryptedPool sync.Pool       // 加密数据缓冲区池
	nextSeq       uint64          // 下一个数据块序号
}

// 进度类型
type Progress struct {
	eofChan      chan struct{}
	progressChan chan *writeData //
}

// 读数据通道
type readData struct {
	seq     uint64 // 数据块序号
	data    []byte // 数据内容
	dataLen uint64 // 数据长度
	eof     error  // 结束标志
	err     error  // 错误信息
}

type writeData struct {
	seq      uint64 // 数据块序号
	data     []byte // 加密后数据
	dataLen  uint64 // 数据长度
	fileSize uint64 // 总文件大小（仅首包携带）
	eof      error  // 结束标志
	err      error  // 异常信息
}

// NewEncryptor 创建加密器实例
func NewEncryptor(pwd string) *Encryptor {
	// 根据密码计算步长和在blockSize范围内的整数个步长的数据。
	var step int64
	stepNum := 0
	for _, v := range pwd {
		stepNum += int(v)
	}
	step = (int64(blockSize) / int64(stepNum)) * int64(stepNum)
	return &Encryptor{
		pwd:          pwd,
		step:         step,
		readChan:     make(chan *readData, maxConcurrency*2),
		writeChan:    make(chan *writeData, maxConcurrency),
		sequenceChan: make(chan *writeData, bufferWindow),
		cancelChan:   make(chan struct{}),
		bufferPool: sync.Pool{
			New: func() interface{} { return make([]byte, blockSize) },
		},
		encryptedPool: sync.Pool{
			New: func() interface{} { return make([]byte, blockSize) },
		},
	}
}

// NewProgress 创建进度类型实例
func NewProgress() *Progress {
	return &Progress{
		eofChan:      make(chan struct{}),
		progressChan: make(chan *writeData, maxConcurrency),
	}
}

// Encrypt 主加密方法
func (e *Encryptor) Encrypt(srcPath, destPath string, pg *Progress) error {
	// 打开文件
	srcFile, err := os.Open(srcPath)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	destFile, err := os.OpenFile(destPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	if err != nil {
		return err
	}
	defer destFile.Close()

	e.workerGroup.Add(2)
	// 启动流水线 这种复制主要逻辑的协程的全过程都需要完善的逻辑
	go e.readWorker(srcFile)          // 读取协程
	go e.sequenceWorker(destFile, pg) // 顺序写入协程

	// 加密工作协程
	// 多协程执行的函数，要找到函数自然运行完成的逻辑路线。
	// 这种协程的结束和开启是属于通知型的。
	e.workerGroup.Add(maxConcurrency)
	for i := 0; i < maxConcurrency; i++ {
		go e.processWorker()
	}

	return e.monitorErrors(destFile) // 错误监控
}

// readWorker 数据读取协程
func (e *Encryptor) readWorker(srcFile *os.File) {
	defer e.workerGroup.Done()
	defer close(e.readChan)

	fileInfo, _ := srcFile.Stat()

	// 初始化第一个数据包携带文件大小
	e.sequenceChan <- &writeData{fileSize: uint64(fileInfo.Size())}
	seq := uint64(0)
	for {
		select {
		case <-e.cancelChan:
			return
		default:
			buf := e.bufferPool.Get().([]byte)
			buf = buf[:e.step]
			// 这里读取文件完成后，并没有io.eof的结束信号，需要手动获得。
			n, err := srcFile.Read(buf)
			if n < int(e.step) && err == nil {
				err = io.EOF
			}
			// 如果读取数据报错，结束程序
			if err != nil && err != io.EOF {
				e.writeChan <- &writeData{err: err}
				return
			}
			// 截断有效数据并拷贝
			data := make([]byte, n)
			copy(data, buf[:n])
			e.bufferPool.Put(buf)
			seq = atomic.AddUint64(&e.nextSeq, 1) - 1

			// fmt.Println("readWorker", n, err)
			// 构造读取数据包
			rd := &readData{
				// 设置读取的数据块的序号为加密结构体中的下一个序号的值，并将nextSeq加一。
				seq:  seq,
				data: data,
				eof:  err,
				err:  err,
			}

			// 发送到处理通道
			select {
			case e.readChan <- rd:
			case <-e.cancelChan:
				return
			}
			// 非多协程的协程一定要找到函数正常结束的逻辑
			if err == io.EOF {
				return
			}
			seq++
		}
	}
}

// processWorker 数据处理协程
func (e *Encryptor) processWorker() {
	defer e.workerGroup.Done()
	for {
		select {
		case <-e.cancelChan:
			return
		case rd, ok := <-e.readChan:
			if !ok {
				return
			}
			// 错误处理,排除正常结束
			if rd.err != nil && rd.eof != io.EOF {
				e.writeChan <- &writeData{err: rd.err}
				return
			}
			// 执行加密操作
			encrypted := e.encryptData(rd.data)

			// 计算写入位置
			// pos := atomic.AddUint64(&e.writePos, uint64(len(encrypted))) - uint64(len(encrypted))

			// 构造写入数据包
			wd := &writeData{
				seq:     rd.seq,
				data:    encrypted,
				dataLen: uint64(len(rd.data)),
				eof:     rd.eof,
			}

			// 发送到顺序控制通道
			select {
			case e.sequenceChan <- wd:
			case <-e.cancelChan:
				return
			}
			e.bufferPool.Put(rd.data)
		}
	}

}

// encryptData 执行加密逻辑
func (e *Encryptor) encryptData(data []byte) []byte {
	// 从池中获取加密缓冲区
	encrypted := e.encryptedPool.Get().([]byte)
	if cap(encrypted) < len(data) {
		encrypted = make([]byte, len(data))
	} else {
		encrypted = encrypted[:len(data)]
	}
	copy(encrypted, data)

	password := e.pwd
	temp := e.encryptedPool.Get().([]byte)
	if cap(temp) < len(data) {
		temp = make([]byte, len(data))
	} else {
		temp = temp[:len(data)]
	}
	e.applyPassword(encrypted, temp, []byte(password))
	// 交换缓冲区用于下一轮加密
	encrypted, temp = temp, encrypted
	e.encryptedPool.Put(temp)

	return encrypted
}

// applyPassword 应用单个密码加密
func (e *Encryptor) applyPassword(src, dst, pwd []byte) {
	pwdLen := len(pwd)
	if pwdLen == 0 {
		copy(dst, src)
		return
	}

	dataLen := len(src)
	pwdIndex := 0

	// 循环一次要加密的数据，倒序从rd.data中读取。
	for redPos := 0; redPos < dataLen; {
		// 获取单个密码的指定位置的字符。
		tempByteLen := int(pwd[pwdIndex])
		// 根据密码的字符长度进行取余作为循环的pwdIndex
		pwdIndex = (pwdIndex + 1) % pwdLen

		// 如果最后的字符串长度达不到密码单字符ASCII码的长度(ASCII码的值)，重置tempByteLen
		if tempByteLen > dataLen-redPos {
			tempByteLen = dataLen - redPos
		}
		// 顺序读取数据，并按照字符的ASCII码长度进行倒序排列
		for i := 0; i < tempByteLen; i++ {
			dst[redPos+i] = src[redPos+tempByteLen-1-i]
		}
		redPos += tempByteLen
	}
}

// sequenceWorker 顺序写入协程
func (e *Encryptor) sequenceWorker(destFile *os.File, pg *Progress) {
	defer e.workerGroup.Done()
	cache := make(map[uint64]*writeData) // 乱序缓存
	// 当前位置（初始位置0）
	currentSeq := uint64(0)
	for {
		select {
		case <-pg.eofChan: //如果进度100%通知写协程结束
			return
		case <-e.cancelChan:
			return
		case wd, ok := <-e.sequenceChan:
			if !ok {
				return
			}
			// fmt.Println("sequenceWorker", wd.eof, wd.filePos, wd.seq)
			// 处理错误，如果是异常数据，发送到writeChan被异常监控进程检测。
			if wd.err != nil {
				e.writeChan <- wd
				return
			}
			// 初始化文件大小
			if wd.fileSize > 0 {
				// 将文件初始信息发给进度条。
				pg.progressChan <- wd
				continue
			}
			// fmt.Println("sequenceWorker", wd.eof, wd.filePos, wd.seq)
			cache[wd.seq] = wd
			e.processCache(&currentSeq, cache, destFile, pg)
		}
	}
}

// processCache 处理缓存中的数据
func (e *Encryptor) processCache(currentSeq *uint64, cache map[uint64]*writeData, destFile *os.File, pg *Progress) {
	for {
		// 检查当前期望的序号
		wd, exists := cache[*currentSeq]
		if !exists {
			return
		}

		// 如果存在，直接写入
		if _, err := destFile.Write(wd.data); err != nil {
			e.writeChan <- &writeData{err: err}
			return
		}

		// 发送进度更新
		select {
		case pg.progressChan <- wd:
		default:
			// 丢弃进度更新避免阻塞
		}
		// 清理资源
		e.encryptedPool.Put(wd.data)
		delete(cache, *currentSeq)
		*currentSeq++
		// 如果最后的数据已经写入成功，结束协程。
		// 非多协程的协程一定要找到函数正常结束的逻辑
		if wd.eof == io.EOF {
			// 通知写协程结束
			close(pg.eofChan)
			return
		}
	}
}

// monitorErrors 错误监控协程
func (e *Encryptor) monitorErrors(destFile *os.File) error {
	var firstErr error

	// 等待所有协程完成
	// 挂起一个等待协程
	go func() {
		e.workerGroup.Wait()
		// 关闭数据写入通道
		close(e.writeChan)
		close(e.sequenceChan)
		close(e.cancelChan)
	}()

	for {
		select {
		case <-e.cancelChan:
			return firstErr
			// 正常完成
		case wd, ok := <-e.writeChan:
			if !ok {
				continue
			}
			if wd.err != nil && wd.err != io.EOF {
				firstErr = wd.err
				// 触发快速终止
				// 所有的协程将终止
				close(e.cancelChan)
				// 清理目标文件
				destFile.Truncate(0)
			}
			return firstErr

		}
	}
}
